#include "profile.h"
#include "ui_profile.h"
#include <qjson/parser.h>
#include <QFile>
#include <QTextStream>
#include <QPluginLoader>
#include <QLabel>
#include <QDebug>
#include <QMessageBox>
#include <QSqlError>
#include "profilegroupseparator.h"

Profile::Profile(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::Profile)
{
    ui->setupUi(this);
    m_last_id = 0;
//    m_last_type = 0; // initialization in loadConfig
    loadConfig();

    loadIdTypeMap();

    connect(ui->writeButton, SIGNAL(clicked()),
            this, SLOT(writeProfile()));
}

Profile::~Profile()
{
    delete ui;
}

void Profile::loadIdTypeMap()
{
    sql.exec("SELECT `id`,`type` FROM `people` WHERE 1;");
    while (sql.next()) {
        int id = sql.value(0).toInt();
        int type = sql.value(1).toInt();
        m_peopleIdType.insert(id, type);
    }
}

void Profile::loadProfile(int id)
{
    int type = m_peopleIdType.value(id); // people type
    QString query = QString("%1 FROM `people` WHERE `id`='%2'").arg(m_peopleTypes[type].selectQuery).arg(id);
    qDebug() << query;
    if (!sql.exec(query)) {
        QMessageBox::warning(this, tr("Warning"), tr("Can't load profile, sql error"));
        m_last_id = 0;
        hide();
        return;
    }

    m_peopleTypes[m_last_type].widget->hide();
    m_peopleTypes[type].widget->show();
    m_last_type = type;

    QVector<QWidget *> delegates = m_peopleTypes[type].delegates;
    if (sql.next()) {
        for (int i = 0; i < delegates.count(); ++i) {
            QWidget *delegate = delegates[i];
            int pluginId = delegate->objectName().toInt();
            DatabaseField *df = m_fieldInterfaces[pluginId];
            df->setData(delegate, sql.value(i));
        }
        m_last_id = id;
    }
}

void Profile::writeProfile()
{
    if (m_last_id == 0) {
        return;
    }

    int type = m_peopleIdType[m_last_id];
    QString query = m_peopleTypes[type].updateQuery;

    QVector<QWidget *> delegates = m_peopleTypes[type].delegates;
    for (int i = 0; i < delegates.count(); ++i) {
        QWidget *delegate = delegates[i];
        int pluginId = delegate->objectName().toInt();
        DatabaseField *df = m_fieldInterfaces[pluginId];
        query = query.arg(df->data(delegate).toString());
    }
    query += " WHERE `id`='" + QString::number(m_last_id) + '\'';

    qDebug() << query;
    if (sql.exec(query)) {
        QMessageBox::information(this, tr("Information"), tr("Profile updated"));
    } else {
        QMessageBox::warning(this, tr("Warning"), tr("Update error, call administrator"));
        qWarning() << sql.lastError().text();
    }
}

void Profile::loadConfig()
{
    QFile file("profile.json");
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qWarning() << "Can't open profile declaration, file didn't exist!";
        return;
    }
    QTextStream in(&file);
    in.setCodec("UTF-8");
    QString json = in.readAll();
    file.close();
    bool ok;
    QJson::Parser parser;
    m_conf = parser.parse(json.toUtf8(), &ok).toMap();
    if (!ok) {
        qWarning() << "profile.json parse fail";
        return;
    }

    QHash<QString, int> pluginIdByName = loadPlugins();
    QVariantList fields = m_conf["fields"].toList();


    QHash<int, people_type2_t> peopleType2;
    QVector<int> peopleTypes;

    for (int i = 0; i < fields.count(); ++i) {
        QVariantMap field = fields[i].toMap(); // get field description from config
        QString fieldType = field["type"].toString();

        // insert separator
        if (fieldType == "group") {
            QVariantList peopleTypesV = field["types"].toList();
            peopleTypes.clear();
            for (int i = 0; i < peopleTypesV.count(); ++i) {
                int type = peopleTypesV[i].toInt();
                peopleTypes.push_back(type);

                ProfileGroupSeparator *separator = new ProfileGroupSeparator;
                separator->setText(field["name"].toString());

                if (!peopleType2.contains(type)) { // create people type
                    people_type2_t pt2 = {0, 0, 0};
                    peopleType2.insert(type, pt2);

                    people_type_t pt;
                    pt.lay = new QGridLayout;
                    pt.widget = new QWidget;
                    pt.widget->setLayout(pt.lay);
                    ui->fieldsLayout->addWidget(pt.widget, 0); // one on another ;)
                    pt.widget->hide();
                    pt.selectQuery = "SELECT ";
                    pt.updateQuery = "UPDATE `people` SET ";
                    m_peopleTypes.insert(type, pt);
                }
                people_type_t &pt = m_peopleTypes[type];
                people_type2_t &pt2 = peopleType2[type];
                pt.lay->addWidget(separator, pt2.gridRow, pt2.gridCol, 1, 2);
                if (++pt2.gridRow >= 10) {
                    pt2.gridCol += 2;
                    pt2.gridRow = 0;
                }
            }
            continue;
        }

        if (!pluginIdByName.contains(fieldType)) { // check if plugin loaded
            qWarning() << "Field type" << fieldType << "didn't exist!";
            continue;
        }

        int pluginId = pluginIdByName[fieldType];
        DatabaseField *df = m_fieldInterfaces[pluginId];

        QString fieldName = field["dbname"].toString();
        QVariantMap fieldProperties = field["properties"].toMap();

        for (int j = 0; j < peopleTypes.count(); ++j) {
            int type = peopleTypes[j];

            people_type_t &pt = m_peopleTypes[type];
            people_type2_t &pt2 = peopleType2[type];

            QWidget *delegate = df->createDelegate(fieldProperties);
            if (field.contains("editable"))
                df->setEditable(delegate, field["editable"].toBool());
            delegate->setObjectName(QString::number(pluginId));
            pt.delegates.append(delegate);

            // checkbox - activate
            if (field.contains("id")) {
                QString jsonId = field["id"].toString();
                if (!m_jsonIdDelegate.contains(jsonId))
                    m_jsonIdDelegate.insert(jsonId, delegate);
            }

            if (field.contains("active")) {
                QString jsonId = field["active"].toString();
                if (m_jsonIdDelegate.contains(jsonId)) {
                    QWidget *senderDelegate = m_jsonIdDelegate[jsonId];
                    int pluginId = senderDelegate->objectName().toInt();
                    DatabaseField *sdf = m_fieldInterfaces[pluginId];
                    sdf->connectToDataChange(senderDelegate, delegate, SLOT(setEnabled(bool)));
                }
            }

            QLabel *label = new QLabel(field["name"].toString());
            label->setObjectName("profileLabel");

            pt.lay->addWidget(label, pt2.gridRow, pt2.gridCol);
            pt.lay->addWidget(delegate, pt2.gridRow, pt2.gridCol + 1);
            if (++pt2.gridRow >= 10) {
                pt2.gridCol += 2;
                pt2.gridRow = 0;
            }

            pt.selectQuery += '`' + fieldName + "`,";
            pt.updateQuery += '`' + fieldName + "`='%" + QString::number(pt2.updateIdx) + "\',";
            pt2.updateIdx++;
        }
    }

    // remove extra ',' at the end of each query; initialize last_type
    QMutableHashIterator<int, people_type_t> it(m_peopleTypes);
    while (it.hasNext()) {
        it.next();
        m_last_type = it.key();

        people_type_t &pt = it.value();
        pt.selectQuery.remove(pt.selectQuery.length() - 1, 1);
        pt.updateQuery.remove(pt.updateQuery.length() - 1, 1);
    }
}

QHash<QString, int> Profile::loadPlugins()
{
    QStringList plugins = m_conf["plugins"].toStringList();
    QString dir = m_conf["dir"].toString();
    QString extension = m_conf["extension"].toString();
    QHash<QString, int> pluginIdByName;

    int id = 0;
    foreach (QString plugin, plugins) {
        QString path = dir + plugin + extension;
        QPluginLoader *loader = new QPluginLoader(path, this);
        if (loader->load()) {
            DatabaseField *df = qobject_cast<DatabaseField *>(loader->instance());
            if (df) {
                m_fieldInterfaces.insert(id, df);
                pluginIdByName.insert(plugin, id);

                qDebug() << "Field plugin" << plugin << "loaded";
            } else {
                qWarning() << "Field plugin loader for" << path << "returned null pointer!";
            }
        } else {
            qWarning() << "Failed to load field plugin" << path << ":" << loader->errorString();
        }
        id++;
    }
    return pluginIdByName;
}
